1
2
3
4
5
6
7
8
9
10
11
12
13 package org.apache.tapestry5.internal.services.javascript;
14
15 import org.apache.tapestry5.SymbolConstants;
16 import org.apache.tapestry5.internal.services.AssetDispatcher;
17 import org.apache.tapestry5.internal.services.RequestConstants;
18 import org.apache.tapestry5.internal.services.ResourceStreamer;
19 import org.apache.tapestry5.ioc.IOOperation;
20 import org.apache.tapestry5.ioc.OperationTracker;
21 import org.apache.tapestry5.ioc.Resource;
22 import org.apache.tapestry5.ioc.annotations.Symbol;
23 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
24 import org.apache.tapestry5.services.Dispatcher;
25 import org.apache.tapestry5.services.LocalizationSetter;
26 import org.apache.tapestry5.services.PathConstructor;
27 import org.apache.tapestry5.services.Request;
28 import org.apache.tapestry5.services.Response;
29 import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
30 import org.apache.tapestry5.services.javascript.ModuleManager;
31
32 import javax.servlet.http.HttpServletResponse;
33 import java.io.IOException;
34 import java.util.EnumSet;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.Set;
39
40
41
42
43
44
45
46
47 public class ModuleDispatcher implements Dispatcher
48 {
49 private final ModuleManager moduleManager;
50
51 private final ResourceStreamer streamer;
52
53 private final OperationTracker tracker;
54
55 private final JavaScriptStackSource javaScriptStackSource;
56
57 private final JavaScriptStackPathConstructor javaScriptStackPathConstructor;
58
59 private final LocalizationSetter localizationSetter;
60
61 private final String requestPrefix;
62
63 private final String stackPathPrefix;
64
65 private final boolean compress;
66
67 private final Set<ResourceStreamer.Options> omitExpiration = EnumSet.of(ResourceStreamer.Options.OMIT_EXPIRATION);
68
69 private Map<String, String> moduleNameToStackName;
70
71 public ModuleDispatcher(ModuleManager moduleManager,
72 ResourceStreamer streamer,
73 OperationTracker tracker,
74 PathConstructor pathConstructor,
75 JavaScriptStackSource javaScriptStackSource,
76 JavaScriptStackPathConstructor javaScriptStackPathConstructor,
77 LocalizationSetter localizationSetter,
78 String prefix,
79 @Symbol(SymbolConstants.ASSET_PATH_PREFIX)
80 String assetPrefix,
81 boolean compress)
82 {
83 this.moduleManager = moduleManager;
84 this.streamer = streamer;
85 this.tracker = tracker;
86 this.javaScriptStackSource = javaScriptStackSource;
87 this.javaScriptStackPathConstructor = javaScriptStackPathConstructor;
88 this.localizationSetter = localizationSetter;
89 this.compress = compress;
90
91 requestPrefix = pathConstructor.constructDispatchPath(compress ? prefix + ".gz" : prefix) + "/";
92 stackPathPrefix = pathConstructor.constructDispatchPath(assetPrefix, RequestConstants.STACK_FOLDER) + "/";
93 }
94
95 public boolean dispatch(Request request, Response response) throws IOException
96 {
97 String path = request.getPath();
98
99 if (path.startsWith(requestPrefix))
100 {
101 String extraPath = path.substring(requestPrefix.length());
102
103 Locale locale = request.getLocale();
104
105 if (!handleModuleRequest(locale, extraPath, response))
106 {
107 response.sendError(HttpServletResponse.SC_NOT_FOUND, String.format("No module for path '%s'.", extraPath));
108 }
109
110 return true;
111 }
112
113 return false;
114
115 }
116
117 private boolean handleModuleRequest(Locale locale, String extraPath, Response response) throws IOException
118 {
119
120
121
122
123
124 int dotx = extraPath.lastIndexOf('.');
125
126 if (dotx < 0)
127 {
128 return false;
129 }
130
131 if (!extraPath.substring(dotx + 1).equals("js"))
132 {
133 return false;
134 }
135
136 final String moduleName = extraPath.substring(0, dotx);
137
138 String stackName = findStackForModule(moduleName);
139
140 if (stackName != null)
141 {
142 localizationSetter.setNonPersistentLocaleFromLocaleName(locale.toString());
143 List<String> libraryUrls = javaScriptStackPathConstructor.constructPathsForJavaScriptStack(stackName);
144 if (libraryUrls.size() == 1)
145 {
146 String firstUrl = libraryUrls.get(0);
147 if (firstUrl.startsWith(stackPathPrefix))
148 {
149 response.sendRedirect(firstUrl);
150 return true;
151 }
152 }
153 }
154
155 return tracker.perform(String.format("Streaming %s %s",
156 compress ? "compressed module" : "module",
157 moduleName), new IOOperation<Boolean>()
158 {
159 public Boolean perform() throws IOException
160 {
161 Resource resource = moduleManager.findResourceForModule(moduleName);
162
163 if (resource != null)
164 {
165
166
167 return streamer.streamResource(resource, compress ? "z" : "", omitExpiration);
168 }
169
170 return false;
171 }
172 });
173 }
174
175 private String findStackForModule(String moduleName)
176 {
177 return getModuleNameToStackName().get(moduleName);
178 }
179
180 private Map<String, String> getModuleNameToStackName()
181 {
182
183 if (moduleNameToStackName == null)
184 {
185 moduleNameToStackName = CollectionFactory.newMap();
186
187 for (String stackName : javaScriptStackSource.getStackNames())
188 {
189 for (String moduleName : javaScriptStackSource.getStack(stackName).getModules())
190 {
191 moduleNameToStackName.put(moduleName, stackName);
192 }
193 }
194 }
195
196 return moduleNameToStackName;
197 }
198 }